package com.hxk.juc.thread.syn8d;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Synchronized和ReentrantLock的区别：
 *  1、构成
 *      Synchronized是jvm的关键字
 *      ReentrantLock是juc的一个类
 *  2、使用方式
 *      Synchronized不需要使用者关心锁的释放
 *      ReentrantLock需要使用者手动的调用lock.unlock()方法来释放锁
 *  3、是否可中断
 *      Synchronized不可以中断，除非抛出异常或者正常运行完成。
 *      ReentrantLock可以通trylock和interrupt()实现手动中断锁
 *  4、是否可以实现公平锁
 *      Synchronized不可以
 *      ReentrantLock可以实现公平锁，在构造的时候传人一个boolean值
 *  5、是否可以实现多条件绑定
 *      Synchronized不可以
 *      ReentrantLock可以通过Condition实现多个条件绑定，可以实现精准唤醒
 *
 *  本case将实现A、B、C三个线程，首先由A线程打印5次后，再由B线程打印10次后，再由C线程打印15次后，再调用A线程打印5次。。。。。，这样循环10次
 *  由于使用Synchronized+wait+notfiy实现比较麻烦，也早已过时，这里采用ReentrantLock+Condition+await+signal实现
 */
public class SyncAndLockCase {

    public static void main(String[] args) {
        MyRes myRes = new MyRes();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                myRes.print(5, 0, 1);
            }},"A").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                myRes.print(10, 1, 2);
            }},"B").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                myRes.print(15, 2, 0);
            }},"C").start();
    }
}

class MyRes {

    private volatile int num = 0;

    private Lock lock = new ReentrantLock();
    private Condition[] conditions = {lock.newCondition(),lock.newCondition(),lock.newCondition()};

    /**
     * 打印方法
     * @param printNum 要打印的次数
     * @param awaitVal 线程等待的值
     * @param signalVal 线程唤醒的值
     */
    public void print(final int printNum, final int awaitVal, final int signalVal){
        lock.lock();
        try {
            //判断
            while (num != awaitVal){
                conditions[awaitVal].await();
            }
            //干活
            for (int i = 1; i <= printNum; i++) {
                System.out.println(Thread.currentThread().getName()+"\t "+i);
            }
            //通知
            num = signalVal;
            conditions[signalVal].signal();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

}